# 湖南声仪的声级计，使用的是modebus协议的
try:
    import os,sys,time
    import serial
    import binascii

except ImportError:
    print(">>> lib not found !!!")
    print(">>> Please pip3 install pyserial !")
    exit(1)

class HY130E:
    
    def __init__(self,dev,bsp):
        self.init(dev,bsp)
        
        
    def __del__(self):
        self.ser.close() #关闭串口
        
    # 初始化通信接口
    def init(self,dev,bsp):
        # 打开串口
        self.ser=serial.Serial(dev,bsp,
                            bytesize=serial.EIGHTBITS,    # 数据位：8位
                            parity=serial.PARITY_NONE,    # 校验位：无
                            stopbits=serial.STOPBITS_ONE, # 停止位：1位
                            timeout=None)                 # 读超时时间
        # 判断是否打开成功
        if self.ser:
            self.addr = self.get_addr()
            if self.addr:
                print("打开串口成功,串口详情参数：",self.ser)
                print(">>> 设备地址为%s"%self.addr)
            else:
                print(">>> 设备异常！！！")
        else:
            print("串口打开失败！")
        
        
    def get_addr(self):
        # 获取设备地址
        result=self.ser.write(bytes.fromhex('00 20 00 68'))
        time.sleep(0.1)
        count=self.ser.inWaiting()
        print(">>> get_addr count:%d"%count)
        if count>0:
            data=self.ser.read(count)
            if data!=b'':
                receive=str(binascii.b2a_hex(data))
                return receive[6:-5]
            else:
                return 999
            
    def set_addr(self,addr):
        instruct='00 10'
        instruct=instruct+' '+addr
        # 计算CRC
        crc_str=self.crc16_checksum(bytes.fromhex(instruct))
        
        # 组包,CRC低位在前
        instruct=instruct+' '+crc_str[2]+crc_str[3]+' '+crc_str[0]+crc_str[1]
        # 发送
        # print(">>>"+instruct)
        result=self.ser.write(bytes.fromhex(instruct))
        time.sleep(0.05)
        count=self.ser.inWaiting()
        if count>0:
            data=self.ser.read(count)
            if data == b'00 10 00 7c':
                print(data)
        
    
    # 复位最大值
    def reset_db_max(self):
        instruct=self.addr+' 06 00 04 00 01'
        crc_str=self.crc16_checksum(bytes.fromhex(instruct))
        instruct=instruct+' '+crc_str[2]+crc_str[3]+' '+crc_str[0]+crc_str[1]
        result=self.ser.write(bytes.fromhex(instruct))
    
    # 查询当前的时间计权模式
    def get_time_weighting(self):
        instruct=self.addr+' 04 00 03 00 01'
        crc_str=self.crc16_checksum(bytes.fromhex(instruct))
        instruct=instruct+' '+crc_str[2]+crc_str[3]+' '+crc_str[0]+crc_str[1]
        result=self.ser.write(bytes.fromhex(instruct))
        
        time.sleep(0.1)
        count=self.ser.inWaiting()
        print(">>> count:%d"%count)
        if count>0:
            data=self.ser.read(count)
            if data!=b'':
                # 将接受的16进制数据格式如b'h\x12\x90xV5\x12h\x91\n4737E\xc3\xab\x89hE\xe0\x16'
                #                      转换成b'6812907856351268910a3437333745c3ab896845e016'
                #                      通过[]去除前后的b'',得到我们真正想要的数据 
                receive=str(binascii.b2a_hex(data))[2:-1]

                if int(receive[6:10],16) == 0:
                    return 'F'
                elif int(receive[6:10],16) == 1:
                    return 'S'
                else:
                    return 99
            else:
                print("no data!!")
                return 99
        
    # 设置时间计权方式,F:125ms，S：1s
    def set_time_weighting(self,mode):
        
        if mode == 'S':
            instruct=self.addr+' 06 00 03 00 01'
            crc_str=self.crc16_checksum(bytes.fromhex(instruct))
            instruct=instruct+' '+crc_str[2]+crc_str[3]+' '+crc_str[0]+crc_str[1]
            result=self.ser.write(bytes.fromhex(instruct))
        elif mode == 'F':
            instruct=self.addr+' 06 00 03 00 00'
            crc_str=self.crc16_checksum(bytes.fromhex(instruct))
            instruct=instruct+' '+crc_str[2]+crc_str[3]+' '+crc_str[0]+crc_str[1]
            result=self.ser.write(bytes.fromhex(instruct))
        else:
            return 99
    # 获取设备型号
    def get_device_type(self):
        instruct=self.addr+' 03 00 03 00 05'
        crc_str=self.crc16_checksum(bytes.fromhex(instruct))
        instruct=instruct+' '+crc_str[2]+crc_str[3]+' '+crc_str[0]+crc_str[1]
        result=self.ser.write(bytes.fromhex(instruct))
        
        time.sleep(0.05)
        count=self.ser.inWaiting()
        print(">>> get_device_type count:%d"%count)
        if count>0:
            data=self.ser.read(count)
            if data!=b'':
                # receive=str(binascii.b2a_hex(data))[2:-1]
                return chr(data[3])+chr(data[4])+chr(data[5])+chr(data[6])+chr(data[7])+chr(data[8])
        
        # data=b'\x01\x03\x05\x41\x41\x41\x41\x41\x41\xB2\x92'
        # print(chr(data[3])+chr(data[4])+chr(data[5])+chr(data[6])+chr(data[7])+chr(data[8]))
        
        
    
    # 获取上次复位到现在的最大值
    def get_db_max(self):
        instruct=self.addr+' 03 00 01 00 01'
        crc_str=self.crc16_checksum(bytes.fromhex(instruct))
        instruct=instruct+' '+crc_str[2]+crc_str[3]+' '+crc_str[0]+crc_str[1]
        result=self.ser.write(bytes.fromhex(instruct))

        time.sleep(0.05)
        count=self.ser.inWaiting()
        print(">>> get_db_max count:%d"%count)
        if count>0:
            data=self.ser.read(count)
            if data!=b'':
                receive=str(binascii.b2a_hex(data))
                return int(receive[6:10],16)
    
    # 获取瞬时值
    def get_db(self):
        instruct=self.addr+' 03 00 00 00 01 '
        crc_str=self.crc16_checksum(bytes.fromhex(instruct))
        instruct=instruct+' '+crc_str[2]+crc_str[3]+' '+crc_str[0]+crc_str[1]
        result=self.ser.write(bytes.fromhex(instruct))

        time.sleep(0.05)
        count=self.ser.inWaiting()
        print(">>> get_db count:%d"%count)
        if count>0:
            data=self.ser.read(count)
            if data!=b'':
                print(data)
                receive=str(binascii.b2a_hex(data))[2:-1]
                return int(receive[6:10],16)
        
    # 返回状态：0校准成功，1校准中，2声级太高，3声级太低，4声级不稳
    def get_calibration_state(self):
        instruct=self.addr+' 04 00 01 00 01'
        crc_str=self.crc16_checksum(bytes.fromhex(instruct))
        instruct=instruct+' '+crc_str[2]+crc_str[3]+' '+crc_str[0]+crc_str[1]
        result=self.ser.write(bytes.fromhex(instruct))
        time.sleep(0.1)
        count=self.ser.inWaiting()
        print(">>> count:%d"%count)
        if count>0:
            data=self.ser.read(count)
            if data!=b'':
                receive=str(binascii.b2a_hex(data))[2:-1]
                return int(receive[6:10],16)
        
    # 校准操作
    # ps:这个操作实际上在本地直接操作设备会比较好
    def calibration(self):
        instruct=self.addr+' 06 00 01 00 01'
        crc_str=self.crc16_checksum(bytes.fromhex(instruct))
        instruct=instruct+' '+crc_str[2]+crc_str[3]+' '+crc_str[0]+crc_str[1]
        result=self.ser.write(bytes.fromhex(instruct))
        
        # 等待6秒以上
        time.sleep(7)
        
        # 读取校准状态
        ret = self.get_calibration_state()
        if ret==0:
            print("校准成功！")
        elif ret == 1:
            print("校准中……")
        elif ret == 2:
            print("声级太高！")
        elif ret == 3:
            print("声级太低！")
        elif ret == 4:
            print("声级不稳！")
        else:
            return 99
        
    # CRC16校验
    def crc16(self,data):
        crc = 0xFFFF
        for byte in data:
            crc ^= byte
            for _ in range(8):
                if crc & 0x0001:
                    crc >>= 1
                    crc ^= 0xA001
                else:
                    crc >>= 1
        return hex(crc)[2:].upper().zfill(4)

    def crc16_checksum(self,hex_string):
        crc = 0xFFFF
        for byte in hex_string:
            crc ^= byte
            for _ in range(8):
                if crc & 0x0001:
                    crc >>= 1
                    crc ^= 0xA001
                else:
                    crc >>= 1
        return hex(crc)[2:].zfill(4)

if __name__ == "__main__":
    print("湖南声仪HY130E声级计例程")
    
    # 实例化
    hy130e_dev = HY130E("/dev/ttyS0",9600)
    # 默认使用F时间计权模式
    # hy130e_dev.set_time_weighting('F')
    # print(hy130e_dev.get_time_weighting())
    
    # print(hy130e_dev.get_db())
    
    # print(hy130e_dev.get_device_type())
    
    # 修改设备地址
    hy130e_dev.set_addr("01")
    print(hy130e_dev.get_addr())
    
    # hy130e_dev.reset_db_max()
    # print(hy130e_dev.get_db_max())
    
    # hy130e_dev.calibration()
    
    # 相关操作pkill -f meter
    # 获取瞬时值
    while True:
        print(hy130e_dev.get_db())
        # print(hy130e_dev.get_db_max())
        time.sleep(1)
    # hy130e_dev.reset_db_max()
    
    # hy108c_dev.calibration()
    
